package com.ibeeking.found.msg.rest.service.impl;

import com.ibeeking.found.common.entity.msg.SmsMsgSendParam;
import com.ibeeking.found.msg.api.feign.MsgMemberCClient;
import com.ibeeking.found.msg.rest.config.prop.SmsFrequencyProp;
import com.ibeeking.found.msg.rest.service.*;
import com.ibeeking.found.msg.service.common.bo.*;
import com.ibeeking.found.msg.service.common.constants.RedisKeyConstant;
import com.ibeeking.found.msg.service.common.param.MsgNoticeParam;
import com.ibeeking.found.msg.service.common.param.MsgSendParam;
import com.ibeeking.found.msg.service.common.vo.SmsTemplateVO;
import com.ibeeking.nematos.constants.enums.LogConstant;
import com.ibeeking.nematos.core.utils.SpringBeanUtil;
import com.ibeeking.nematos.exception.BusinessException;
import com.ibeeking.nematos.log.utils.LogUtils;
import com.ibeeking.nematos.msg.sms.aliyun.AliyunSmsService;
import com.ibeeking.nematos.msg.sms.aliyun.dto.SmsResultDto;
import com.ibeeking.nematos.msg.sms.spring.entity.SmsMessage;
import com.ibeeking.nematos.msg.sms.spring.service.SmsService;
import com.ibeeking.nematos.redis.utils.RedisUtils;
import com.ibeeking.nematos.utils.data.Collections3;
import com.ibeeking.nematos.utils.data.RandomUtil;
import com.ibeeking.nematos.utils.data.StringUtils;
import com.ibeeking.nematos.utils.encryption.EncryptUtils;
import com.ibeeking.nematos.utils.id.IdGen;
import com.ibeeking.nematos.utils.json.JsonUtils;
import com.ibeeking.nematos.utils.result.ResponseCodeEnum;
import com.ibeeking.nematos.utils.result.ResponseResult;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName SendSmsServiceImpl
 * @Description 发送短信实现类
 * @Author ibeeking
 * @Date 2020-12-10 9:21
 **/
@Service
public class SendSmsServiceImpl implements ISendSmsService {

    @Resource
    private IOpenService openServiceImpl;

    @Resource
    private ISmsConfigSignService smsConfigSignServiceImpl;

    @Resource
    private ISmsTemplateService smsTemplateServiceImpl;

    @Resource
    private IMsgTypeService msgTypeServiceImpl;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private SmsFrequencyProp smsFrequencyProp;

    @Resource
    private ISmsRecordService smsRecordServiceImpl;

    @Resource
    private MsgMemberCClient msgMemberCClient;

    /**
     * 短信通知
     *
     * @param tenantId
     * @param templateCode
     * @return
     */
     @Override
     public Boolean sendSmsTemplateMessage(Integer tenantId, String templateCode, List<String> toUser, Map<String, String> paramMap) {
            if(StringUtils.isNotEmpty(templateCode)){
                SmsTemplateVO smsTemplateVO = smsTemplateServiceImpl.queryTemplateByCode(templateCode);
                if(smsTemplateVO!=null) {
                    String signName = smsTemplateVO.getSignName();
                    SmsMessage message = new SmsMessage();
                    message.setPhoneNumbers(toUser.toArray(new String[toUser.size()]));
                    message.setTemplateId(templateCode);
                    if(paramMap!=null && paramMap.size()>0) {
                        Map<String, String> smsParamMap = new HashMap<>();
                        String content = smsTemplateVO.getTemplateContent();
                        if(StringUtils.isNotEmpty(content)){
                            paramMap.keySet().stream().forEach(d->{
                                String key = "${".concat(d).concat("}");
                                if(content.trim().indexOf(d) >=0){
                                    smsParamMap.put(d, paramMap.get(d));
                                }
                            });
                        }
                        if(smsParamMap!=null && smsParamMap.size()>0) {
                            message.setTemplateParam(JsonUtils.toJsonStr(smsParamMap));
                        }
                    }
                    message.setSignName(signName);
                    SmsService smsService = openServiceImpl.getSmsService(Long.valueOf(tenantId));

                    LogUtils.info(">>>>>>>sendSmsTemplateMessage message:{}", JsonUtils.toJsonStr(message));
                    smsService.sendSms( message);
                }
            }
            return true;
     }

    @Override
    public Boolean sendSmsSimpleMessage(MsgNoticeParam param) {
        SmsMsgSendParam msg = JsonUtils.fromJson(param.getContent(), SmsMsgSendParam.class);
        //参数校验
        if (checkParam(msg)) {
            return true;
        }
        AliyunSmsService smsService = getService(String.valueOf(msg.getConfigId()));
        //获取签名
        SmsConfigSignBO smsConfigSignBO = smsConfigSignServiceImpl.queryById(msg.getSignId());
        String templateCode = msg.getTemplateCode();
        if (StringUtils.isBlank(templateCode)) {
            SmsTemplateBO smsTemplateBO = smsTemplateServiceImpl.queryById(msg.getTemplateId());
            if (null == smsTemplateBO) {
                return true;
            }
            templateCode = smsTemplateBO.getTemplateCode();
        }
        String mobile = msg.getMobile();
        if (StringUtils.isBlank(mobile)) {
            ResponseResult<List<String>> responseResult = msgMemberCClient.queryMobiles(Collections.singletonList(msg.getMemberId()));
            if (null == responseResult || !ResponseCodeEnum.SUCCESS.getCode().equals(responseResult.getCode())) {
                return false;
            } else {
                if (Collections3.isEmpty(responseResult.getData())) {
                    return true;
                } else {
                    mobile = responseResult.getData().get(0);
                }
            }
        }
        ResponseResult<SmsResultDto> responseResult = smsService.sendSms(mobile, smsConfigSignBO.getSignName(), templateCode, msg.getData());
        smsRecordServiceImpl.smsMsgRecord(Integer.valueOf(ThreadContext.get(LogConstant.TENANT_ID)), null, msg.getMobile(), msg.getData(), responseResult);
        return true;
    }

    @Override
    public String verificationCode(Integer tenantId, MsgSendParam param) {
        String key = IdGen.uuid() + param.getMobile();
        if (check(String.valueOf(tenantId), param.getSmsTypeCode().name(), param.getMobile())) {
            sendVerificationCode(key, param.getMobile(), param.getSmsTypeCode().name(), tenantId);
        } else {
            throw new BusinessException("验证码获取次数过多，请稍后再试");
        }
        return key;
    }

    @Override
    public Boolean clearVerificationCode(String key, String mobile) {
        return redisUtils.expire(key + mobile, 0);
    }

    @Override
    public Boolean checkVerificationCode(String key, String mobile, String code) {
        String code1 = queryVerificationCode(key, mobile);
        if (StringUtils.isBlank(code1) || StringUtils.isBlank(code) || !code1.equals(code)) {
            return false;
        } else {
            redisUtils.expire(key + mobile, 0);
            return true;
        }
    }

    @Override
    public String queryVerificationCode(String key, String mobile) {
        Object o = redisUtils.get(key + mobile);
        if (null == o) {
            return "";
        } else {
            return o.toString();
        }
    }

    private void sendVerificationCode(String key, String mobile, String smsTypeCode, Integer tenantId) {
        String code = RandomUtil.getRandomNumString(6);
        Map<String, String> param = new HashMap<>(1);
        param.put("code", code);
        //查询发送短信信息
        SendSmsParamBo sendSmsParamBo = msgTypeServiceImpl.querySmsParamByCode(smsTypeCode, tenantId);
        if (null == sendSmsParamBo) {
            throw new BusinessException("消息类型不存在。");
        }
        ResponseResult<SmsResultDto> responseResult = getService(sendSmsParamBo).sendSms(mobile, sendSmsParamBo.getSignName(), sendSmsParamBo.getTemplateCode(), param);
        if (ResponseCodeEnum.SUCCESS.getCode().equals(responseResult.getCode())) {
            redisUtils.set(key + mobile, code, 300L);
        }
        smsRecordServiceImpl.smsMsgRecord(tenantId, sendSmsParamBo.getMsgTypeId(), mobile, param, responseResult);
    }

    private Boolean check(String tenantId, String msgType, String mobile) {
        int day = 0;
        int hour = 0;
        int min = 0;
        try {
            if (redisUtils.tryLock(msgType + tenantId + mobile)) {
                Object o1 = redisUtils.get(RedisKeyConstant.SMS_CHECK + msgType + smsFrequencyProp.getDay() + mobile);
                Long o1Expire = redisUtils.getExpire(RedisKeyConstant.SMS_CHECK + msgType + smsFrequencyProp.getDay() + mobile);
                if (null != o1) {
                    day = (Integer) o1;
                } else {
                    o1Expire = RedisKeyConstant.DAY;
                }
                redisUtils.set(RedisKeyConstant.SMS_CHECK + msgType + smsFrequencyProp.getDay() + mobile, day + 1, o1Expire);

                Object o2 = redisUtils.get(RedisKeyConstant.SMS_CHECK + msgType + smsFrequencyProp.getHour() + mobile);
                Long o2Expire = redisUtils.getExpire(RedisKeyConstant.SMS_CHECK + msgType + smsFrequencyProp.getHour() + mobile);
                if (null != o2) {
                    hour = (Integer) o2;
                } else {
                    o2Expire = RedisKeyConstant.HOUR;
                }
                redisUtils.set(RedisKeyConstant.SMS_CHECK + msgType + smsFrequencyProp.getHour() + mobile, hour + 1, o2Expire);

                Object o3 = redisUtils.get(RedisKeyConstant.SMS_CHECK + msgType + smsFrequencyProp.getMin() + mobile);
                Long o3Expire = redisUtils.getExpire(RedisKeyConstant.SMS_CHECK + msgType + smsFrequencyProp.getMin() + mobile);
                if (null != o3) {
                    min = (Integer) o3;
                } else {
                    o3Expire = 60L;
                }
                redisUtils.set(RedisKeyConstant.SMS_CHECK + msgType + smsFrequencyProp.getMin() + mobile, min + 1, o3Expire);
            }
        } finally {
            redisUtils.unlock(msgType + tenantId + mobile);
        }

        if (day >= Integer.parseInt(smsFrequencyProp.getDay()) || hour >= Integer.parseInt(smsFrequencyProp.getHour()) || min >= Integer.parseInt(smsFrequencyProp.getMin())) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 参数校验
     *
     * @param param
     * @return
     */
    private Boolean checkParam(SmsMsgSendParam param) {
        if (null == param.getConfigId()) {
            LogUtils.warn("短信配置必选:[{}]", JsonUtils.toJsonStr(param));
            return true;
        }
        if (null == param.getSignId()) {
            LogUtils.warn("短信签名必选:[{}]", JsonUtils.toJsonStr(param));
            return true;
        }
        if (null == param.getTemplateId() && StringUtils.isBlank(param.getTemplateCode())) {
            LogUtils.warn("短信模板必选:[{}]", JsonUtils.toJsonStr(param));
            return true;
        }
        if (null == param.getMemberId() && StringUtils.isBlank(param.getMobile())) {
            LogUtils.warn("手机号必填:[{}]", JsonUtils.toJsonStr(param));
            return true;
        }
        if (CollectionUtils.isEmpty(param.getData())) {
            LogUtils.warn("短信内容必填:[{}]", JsonUtils.toJsonStr(param));
            return true;
        }
        return false;
    }

    public static AliyunSmsService getService(String configId) {
        if (SpringBeanUtil.containsBean(configId)) {
            return SpringBeanUtil.getApplicationContext().getBean(configId, AliyunSmsService.class);
        }
        //通过configId查询配置信息
        ISmsConfigService bean = SpringBeanUtil.getBean(ISmsConfigService.class);
        SmsConfigBO smsConfigBO = bean.queryById(Long.valueOf(configId));
        SmsConfigByAliBO smsConfigByAliBO = smsConfigBO.getSmsConfigByAli();
        AliyunSmsService aliyunSmsService = new AliyunSmsService(smsConfigByAliBO.getAccessKey(), EncryptUtils.AesEncrypt.decrypt(smsConfigByAliBO.getAccessSecret(), smsConfigByAliBO.getSecretKey()));
        SpringBeanUtil.registerSingletonBean(configId, aliyunSmsService);
        return aliyunSmsService;
    }


    public static AliyunSmsService getService(SendSmsParamBo sendSmsParamBo) {
        if (SpringBeanUtil.containsBean(sendSmsParamBo.getTenantId() + sendSmsParamBo.getAccessKey())) {
            return SpringBeanUtil.getApplicationContext().getBean(sendSmsParamBo.getTenantId() + sendSmsParamBo.getAccessKey(), AliyunSmsService.class);
        }
        AliyunSmsService aliyunSmsService = new AliyunSmsService(sendSmsParamBo.getAccessKey(), EncryptUtils.AesEncrypt.decrypt(sendSmsParamBo.getAccessSecret(), sendSmsParamBo.getSecretKey()));
        SpringBeanUtil.registerSingletonBean(sendSmsParamBo.getTenantId() + sendSmsParamBo.getAccessKey(), aliyunSmsService);
        return aliyunSmsService;
    }

}
